package com.sdy.healthcode.biz.service.impl;

import com.baomidou.mybatisplus.extension.service.additional.update.impl.LambdaUpdateChainWrapper;
import com.sdy.common.model.BizException;
import com.sdy.common.model.FileObj;
import com.sdy.common.utils.Assert;
import com.sdy.common.utils.DateUtil;
import com.sdy.common.utils.EncodeUtil;
import com.sdy.common.utils.IdCardUtil;
import com.sdy.common.utils.RandomUtil;
import com.sdy.common.utils.StringUtil;
import com.sdy.healthcode.biz.constants.Constants;
import com.sdy.healthcode.biz.constants.HealthAbnormalType;
import com.sdy.healthcode.biz.constants.HealthCodeType;
import com.sdy.healthcode.biz.constants.HealthStatusType;
import com.sdy.healthcode.biz.constants.RedisConstants;
import com.sdy.healthcode.biz.dto.*;
import com.sdy.healthcode.biz.mapper.HealthCodeMapper;
import com.sdy.healthcode.biz.model.ApplyPost;
import com.sdy.healthcode.biz.model.HealthClockIn;
import com.sdy.healthcode.biz.model.HealthCode;
import com.sdy.healthcode.biz.model.User;
import com.sdy.healthcode.biz.service.ApplyPostService;
import com.sdy.healthcode.biz.service.HealthClockInService;
import com.sdy.healthcode.biz.service.HealthCodeService;
import com.sdy.healthcode.biz.service.RegionService;
import com.sdy.healthcode.biz.service.UserService;
import com.sdy.healthcode.biz.utils.ParamChecker;
import com.sdy.healthcode.biz.utils.QrCodeUtil;
import com.sdy.mvc.service.impl.BaseServiceImpl;
import com.sdy.redis.service.RedisService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Lazy;
import org.springframework.integration.redis.util.RedisLockRegistry;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.PostConstruct;
import java.awt.*;
import java.io.File;
import java.io.FileOutputStream;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.UUID;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.Lock;

/**
 * <p>
 * 服务实现类
 * </p>
 *
 * @author zzq
 * @since 2020-02-18
 */
@Slf4j
@Service
public class HealthCodeServiceImpl extends BaseServiceImpl<HealthCode> implements HealthCodeService {
    @Autowired
    private HealthCodeMapper healthCodeMapper;
    @Autowired
    private ApplyPostService applyPostService;
    @Autowired
    private HealthClockInService healthClockInService;
    @Autowired
    private UserService userService;
    @Autowired
    private RedisLockRegistry redisLockRegistry;
    @Autowired
    private RegionService regionService;
    @Autowired
    private RedisService redisService;
    @Value("${res.app.bindphone}")
    private Boolean bindphone;
    @Autowired
    @Lazy
    private HealthCodeService healthCodeService;

    private static final Map<String, Integer> AREA_DANGER_INDEX = new HashMap<>();

    static {
        AREA_DANGER_INDEX.put("湖北", 10);
        AREA_DANGER_INDEX.put("广东", 8);
        AREA_DANGER_INDEX.put("浙江", 6);
    }

    @PostConstruct
    public void init() {
        // 初始化创建二维码存储文件夹
        File dir = new File(Constants.QRCODE_PATH);
        if (!dir.exists()) {
            dir.mkdirs();
        }

    }

    @Override
    public void applyForCodeLockable(UserApplyPost userApplyPost, String redirectUrl) throws BizException {
        // 1.校验表单
        checkApplyFormParam(userApplyPost);
        Lock lock = redisLockRegistry.obtain(RedisConstants.LOCK_APPLY_HEALTHCODE + userApplyPost.getUserId());
        try {
            if (!lock.tryLock(10, TimeUnit.SECONDS)) {
                throw new BizException("操作过于频繁，请稍后再试。");
            }
            try {
                healthCodeService.applyForCode(userApplyPost, redirectUrl);
            } finally {
                lock.unlock();
            }
        } catch (InterruptedException e) {
            log.error("操作过于频繁，请稍后再试。", e);
            throw new BizException("操作过于频繁，请稍后再试。");
        }
    }

    @Override
    @Transactional(transactionManager = "transactionManager", propagation = Propagation.REQUIRES_NEW, rollbackFor = Exception.class)
    public void applyForCode(UserApplyPost userApplyPost, String redirectUrl) throws BizException, InterruptedException {
        // 2.保存表单信息
        ApplyPost applyPost = saveApplyData(userApplyPost);
        // 3.更新用户数据
        updateUserInfo(userApplyPost);
        // 4.评估健康码
        HealthCodeType codeType = evaluate(applyPost);
        // 5.创建健康二维码
        int applyCount = (int) lambdaQuery().eq(HealthCode::getUserId, userApplyPost.getUserId()).isNotNull(HealthCode::getApplyPostId).list()
                .stream().map(HealthCode::getApplyPostId).distinct().count();
        HealthCode healthCode = createHealthCode(codeType, userApplyPost.getType(), userApplyPost.getUserId(), userApplyPost.getApplyUserId(), applyPost.getId(),
                redirectUrl, applyCount + 1, null, "apply:" + applyPost.getId());
        // 6.------------------ 感染 ------------------
        if (userApplyPost.getType().equals(1) || applyPost.getType().equals(2)) {
            infectHealthCode(healthCode, userApplyPost.getFamilyMember());
        }
    }

    @Override
    public void invalidAllCodeForUser(Integer userId) {
        lambdaUpdate().eq(HealthCode::getUserId, userId).in(HealthCode::getType, 1, 2).set(HealthCode::getState, 2).update();
    }

    private void infectHealthCode(HealthCode healthCode, List<FamilyMember> familyMemberList) throws InterruptedException, BizException {
        HealthCodeType codeType = HealthCodeType.from(healthCode.getHcode());
        int level = codeType.getLevel();
        List<HealthCode> familyMemberHealthCodeList = new ArrayList<>();
        Integer sourceInfectUserId = null;
        for (FamilyMember familyMember : familyMemberList) {
            HealthCode familyMemberHealthCode = queryHealthCodeByPhone(familyMember.getPhone());
            if (familyMemberHealthCode != null) {
                familyMemberHealthCodeList.add(familyMemberHealthCode);
                int newLevel = HealthCodeType.from(familyMemberHealthCode.getHcode()).getLevel();
                if (newLevel > level) {
                    level = newLevel;
                    sourceInfectUserId = familyMemberHealthCode.getUserId();
                }
            }
        }
        familyMemberHealthCodeList.add(healthCode);
        HealthCodeType infectedHealthCodeType = HealthCodeType.fromLevel(level);
        for (HealthCode code : familyMemberHealthCodeList) {
            if (level > HealthCodeType.from(code.getHcode()).getLevel()) {
                log.info("用户[{}]可能被用户[{}]感染, {} -> {}", code.getUserId(), sourceInfectUserId, code.getHcode(), infectedHealthCodeType.getCode());
                Lock lock = redisLockRegistry.obtain(RedisConstants.LOCK_APPLY_HEALTHCODE + code.getUserId());
                if (lock.tryLock(10, TimeUnit.SECONDS)) {
                    try {
                        createHealthCode(infectedHealthCodeType, code.getType(), code.getUserId(), healthCode.getUserId(), code.getApplyPostId(),
                        null, null, null, "infect:" + healthCode.getId());
                    } finally {
                        lock.unlock();
                    }
                } else {
                    throw new BizException("操作过于频繁，请稍后再试。");
                }
            }
        }
    }

    @Override
    public HealthCode queryHealthCode(Integer userId) {
        return lambdaQuery()
                .eq(HealthCode::getUserId, userId)
                .eq(HealthCode::getState, 1)
                .one();
    }

    private HealthCode queryHealthCodeByPhone(String phone) {
        User user = userService.getUserByPhone(phone);
        if (user == null) {
            return null;
        }
        return queryHealthCode(user.getId());
    }

    @Override
    public HealthCode queryHealthCodeByToken(String token) {
        return lambdaQuery()
                .eq(HealthCode::getToken, token)
                .eq(HealthCode::getState, 1)
                .one();
    }

    @Override
    public void reGenerateHealthQrCode(HealthCode healthCode, String redirectUrl) {
        log.warn("找不到二维码文件，重新生成。HealthCodeId={}", healthCode.getId());
        HealthCodeType codeType = HealthCodeType.from(healthCode.getHcode());
        String content = buildContentForHealthCode(healthCode, redirectUrl);
        createQrCode(codeType.getColor(), content, healthCode.getQrcodePath());
    }

    @Override
    public void reEvaluateFromClockInData(Integer userId, HealthClockIn currentClockIn) {
        HealthCode currentCode = queryHealthCode(userId);
        if (currentCode == null) {
            return;
        }
        List<HealthClockIn> clockInDataList = healthClockInService.listClockData(
                userId,
                Math.max(Constants.HEALTH_CLOCKIN_DAY_Y_TO_G, Constants.HEALTH_CLOCKIN_DAY_R_TO_G),
                currentClockIn.getClockDate());
        HealthCodeType currentCodeType = HealthCodeType.from(currentCode.getHcode());
        // 查询该次打卡生成的历史健康码
        HealthCode lastValidHealthCode = null;
        if (currentCode.getFromId() != null
                && currentCode.getSource() != null
                && currentCode.getSource().equals("clockin:" + DateUtil.formatDate(currentClockIn.getClockDate()))) {
            lastValidHealthCode = getById(currentCode.getFromId());
        }

        Integer fromId = currentCode.getId();
        // 如果是重新打卡，恢复打卡前健康码
        if (lastValidHealthCode != null && !lastValidHealthCode.getId().equals(currentCode.getId())) {
            log.info("用户[{}]重复打卡[{}]，恢复健康码为[{}]", userId, DateUtil.formatDate(currentClockIn.getClockDate()), lastValidHealthCode.getId());
            lambdaUpdate().eq(HealthCode::getUserId, userId).eq(HealthCode::getState, 1).set(HealthCode::getState, 2).update();
            lambdaUpdate().eq(HealthCode::getId, lastValidHealthCode.getId()).set(HealthCode::getState, 1).update();
            fromId = lastValidHealthCode.getId();
            currentCodeType = HealthCodeType.from(lastValidHealthCode.getHcode());
            currentCode = lastValidHealthCode;
        }
        HealthCodeType newCodeType = evaluate(userId, currentCodeType, clockInDataList, currentCode.getApplyTime(), currentClockIn);
        log.info("用户[{}]打卡，更新健康码为 {}", userId, newCodeType.getCode());
        createHealthCode(newCodeType, currentCode.getType(), userId, userId, currentCode.getApplyPostId(), null, null, fromId,
                "clockin:" + DateUtil.formatDate(currentClockIn.getClockDate()));
    }

    /**
     * 保存申请表单数据
     */
    private ApplyPost saveApplyData(UserApplyPost userApplyPost) throws BizException {
        // 取消旧的申请
        applyPostService.lambdaUpdate()
                .eq(ApplyPost::getUserId, userApplyPost.getUserId())
                .eq(ApplyPost::getState, 1)
                .set(ApplyPost::getState, 2)
                .update();
        // 创建新的申请
        ApplyPost applyPost = formatApplyPost(userApplyPost);
        applyPost.setCreateTime(new Date());
        applyPost.setState(1);
        applyPostService.save(applyPost);
        return applyPost;
    }

    private void updateUserInfo(UserApplyPost applyPost) throws BizException {
        LambdaUpdateChainWrapper<User> wrapper = userService.lambdaUpdate()
                .eq(User::getId, applyPost.getUserId())
                .set(StringUtil.isNotBlank(applyPost.getName()), User::getName, applyPost.getName())
                .set(User::getHsDepartmentId, applyPost.getHsDepartmentId())
                .set(applyPost.getCardType() != null, User::getCardType, applyPost.getCardType())
                .set(StringUtil.isNotBlank(applyPost.getCardId()), User::getCardId, applyPost.getCardId());
        if (applyPost.getTripAddr() != null) {
            wrapper.set(User::getAddr, applyPost.getTripAddr().toString())
                    .set(User::getRegionProvince, applyPost.getTripAddr().getRegionProvince())
                    .set(User::getRegionCity, applyPost.getTripAddr().getRegionCity())
                    .set(User::getRegionArea, applyPost.getTripAddr().getRegionArea())
                    .set(User::getRegionStreet, applyPost.getTripAddr().getRegionStreet())
                    .set(User::getRegionCommunity, applyPost.getTripAddr().getRegionCommunity());
        }
        if (!bindphone) {
            User existUser = userService.getUserByPhone(applyPost.getPhone());
            Assert.isTrue(existUser != null && !existUser.getId().equals(applyPost.getUserId()), "手机号已被使用。");
            wrapper.set(User::getPhone, applyPost.getPhone());
        }
        wrapper.update();
    }

    private ApplyPost formatApplyPost(UserApplyPost userApplyPost) throws BizException {
        List<String> imgUrls = new ArrayList<>();
//        if (userApplyPost.getIdCardImg() != null) {
//            for (String img : userApplyPost.getIdCardImg()) {
//                FileObj fileObj = redisService.get(RedisConstants.FILE_UPLOAD_UUID + img, FileObj.class);
//                Assert.isNull(fileObj, "上传的文件已过期，请重新上传。");
//                imgUrls.add(fileObj.getPath());
//            }
//        }
        return new ApplyPost()
                .setUserId(userApplyPost.getUserId())
                .setName(userApplyPost.getName())
                .setPhone(userApplyPost.getPhone())
                .setCardType(userApplyPost.getCardType())
                .setCardId(userApplyPost.getCardId())
                .setTripAddr(userApplyPost.getTripAddr().toString())
                .setHealthTouchAddrDanger(userApplyPost.getHealthTouchAddrDanger())
                .setHealthTouchPatient(userApplyPost.getHealthTouchPatient())
                .setHealthTouchWuhan(userApplyPost.getHealthTouchWuhan())
                .setHealthAbnormalDetail(String.join("##", userApplyPost.getHealthAbnormalDetail()))
                .setAppointmentTime(userApplyPost.getAppointmentTime())
                .setFamilyMember(FamilyMember.toStr(userApplyPost.getFamilyMember()))
                .setHsDepartmentId(userApplyPost.getHsDepartmentId())
                .setIdCardImg(String.join(";", imgUrls))
                .setType(userApplyPost.getType());
    }

    /**
     * 评估健康码类型
     *
     * @param applyPost 申请数据
     */
    private HealthCodeType evaluate(ApplyPost applyPost) {
        int score = 0;
        // 是否接触确诊病人
        if (Constants.YES_CN.equals(applyPost.getHealthTouchWuhan())) {
            log.info("用户{}评估得分：由于接触武汉人员，直接红码", applyPost.getUserId());
            return HealthCodeType.RED;
        }
        // 是否接触确诊病人
        if (Constants.YES_CN.equals(applyPost.getHealthTouchPatient())) {
            log.info("用户{}评估得分：由于接触确诊病人，直接红码", applyPost.getUserId());
            return HealthCodeType.RED;
        }
        // 是否接触确诊病人
        if (Constants.YES_CN.equals(applyPost.getHealthTouchAddrDanger())) {
            log.info("用户{}评估得分：由于去过疫情地区，直接黄码", applyPost.getUserId());
            return HealthCodeType.YELLOW;
        }
        // 是否健康状态异常
        for (String detail : applyPost.getHealthAbnormalDetail().split("##")) {
            if (StringUtil.isNotBlank(detail)) {
                int tmpScore = 0;
                HealthAbnormalType ab = HealthAbnormalType.from(detail);
                tmpScore += ab.getScore();
                if (tmpScore >= 10) {
                    log.info("用户{}评估得分：由于健康状况得分{}，直接红码", applyPost.getUserId(), tmpScore);
                    return HealthCodeType.RED;
                }
                score += tmpScore;
            }
        }

        if (score <= 0) {
            log.info("用户{}评估得分：{}，绿码", applyPost.getUserId(), score);
            return HealthCodeType.GREEN;
        } else if (score < 10) {
            log.info("用户{}评估得分：{}，黄码", applyPost.getUserId(), score);
            return HealthCodeType.YELLOW;
        } else {
            log.info("用户{}评估得分：{}，红码", applyPost.getUserId(), score);
            return HealthCodeType.RED;
        }
    }

    /**
     * 评估健康码类型
     *
     * @param healthClockIn 打卡数据
     */
    private HealthCodeType evaluate(Integer userId, HealthCodeType currentCodeType, List<HealthClockIn> healthClockIn,
                                    Date lastEvaluateDate, HealthClockIn currentClockIn) {
        HealthCodeType newHealthCodeType = currentCodeType;
        lastEvaluateDate = DateUtil.getDate(DateUtil.formatDate(lastEvaluateDate), DateUtil.DATE_FORMAT);
        // 非红码用户根据此次打卡评估健康码
        if (!currentClockIn.getClockDate().before(lastEvaluateDate) && !HealthCodeType.RED.equals(currentCodeType)) {
            if (HealthStatusType.ILL.equals(HealthStatusType.from(currentClockIn.getHealthStatus()))) {
                if (currentCodeType.equals(HealthCodeType.GREEN)) {
                    log.info("用户[{}]健康打卡[{}]-生病，更新健康码 {} -> Y。", userId, DateUtil.formatDate(currentClockIn.getClockDate()), currentCodeType.getCode());
                    currentCodeType = newHealthCodeType = HealthCodeType.YELLOW;
                }
            }
            if (Constants.YES_CN.equals(currentClockIn.getHealthTouchSuspect())) {
                log.info("用户[{}]健康打卡[{}]-接触湖北等地的人，更新健康码 {} -> R。", userId, DateUtil.formatDate(currentClockIn.getClockDate()), currentCodeType.getCode());
                currentCodeType = newHealthCodeType = HealthCodeType.RED;
            }
            if (Constants.YES_CN.equals(currentClockIn.getHealthTouchInfect())) {
                log.info("用户[{}]健康打卡[{}]-接触确诊病人，更新健康码 {} -> R。", userId, DateUtil.formatDate(currentClockIn.getClockDate()), currentCodeType.getCode());
                currentCodeType = newHealthCodeType = HealthCodeType.RED;
            }
            if (Constants.YES_CN.equals(currentClockIn.getHealthNearInfect())) {
                log.info("用户[{}]健康打卡[{}]-邻近确诊病人，更新健康码 {} -> R。", userId, DateUtil.formatDate(currentClockIn.getClockDate()), currentCodeType.getCode());
                currentCodeType = newHealthCodeType = HealthCodeType.RED;
            }
        }
        if (healthClockIn.get(0).getClockDate().after(lastEvaluateDate)) {
            // 根据连续打卡评估健康码
            if (currentCodeType.equals(HealthCodeType.RED)) {
                int day = Constants.HEALTH_CLOCKIN_DAY_R_TO_G;
                if (healthClockIn.subList(0, day)
                        .stream()
                        .filter(Objects::nonNull)
                        .filter(item -> Constants.NO_CN.equals(item.getHealthTouchInfect()))
                        .filter(item -> Constants.NO_CN.equals(item.getHealthNearInfect()))
                        .filter(item -> Constants.NO_CN.equals(item.getHealthTouchSuspect()))
                        .filter(item -> !HealthStatusType.ILL.equals(HealthStatusType.from(item.getHealthStatus())))
                        .count() == day
                        && healthClockIn.get(day - 1).getClockDate().after(lastEvaluateDate)
                ) {
                    log.info("用户[{}]健康打卡14天[{}]-[{}]，更新健康码 R -> G。",
                            userId,
                            DateUtil.formatDate(healthClockIn.get(day - 1).getClockDate()),
                            DateUtil.formatDate(healthClockIn.get(0).getClockDate()));
                    // 健康打卡14天，红变绿
                    newHealthCodeType = HealthCodeType.GREEN;
                }
            } else if (currentCodeType.equals(HealthCodeType.YELLOW)) {
                int day = Constants.HEALTH_CLOCKIN_DAY_Y_TO_G;
                if (healthClockIn.subList(0, day)
                        .stream()
                        .filter(Objects::nonNull)
                        .filter(item -> Constants.NO_CN.equals(item.getHealthTouchInfect()))
                        .filter(item -> Constants.NO_CN.equals(item.getHealthNearInfect()))
                        .filter(item -> Constants.NO_CN.equals(item.getHealthTouchSuspect()))
                        .filter(item -> !HealthStatusType.ILL.equals(HealthStatusType.from(item.getHealthStatus())))
                        .count() == day
                        && healthClockIn.get(day - 1).getClockDate().after(lastEvaluateDate)
                ) {
                    log.info("用户[{}]健康打卡7天[{}]-[{}]，更新健康码 Y -> G。",
                            userId,
                            DateUtil.formatDate(healthClockIn.get(day - 1).getClockDate()),
                            DateUtil.formatDate(healthClockIn.get(0).getClockDate()));
                    // 健康打卡7天，黄变绿
                    newHealthCodeType = HealthCodeType.GREEN;
                }
            }
        }
        return newHealthCodeType;
    }

    /**
     * 生成健康码
     */
    private HealthCode createHealthCode(HealthCodeType codeType,
                                        Integer type,
                                        Integer userId,
                                        Integer applyUserId,
                                        Integer postId,
                                        String redirectUrl,
                                        Integer applyCount,
                                        Integer fromId,
                                        String source) {
        // 创建用户QR文件夹
        String folderDir = Constants.QRCODE_PATH + "/" + userId;
        File dir = new File(folderDir);
        if (!dir.exists()) {
            dir.mkdirs();
        }
        String filePath = folderDir + "/" + System.currentTimeMillis() + "_" + RandomUtil.produceNumber(5) + "." + Constants.QRCODE_FORMAT;
        String tk = "0001" + UUID.randomUUID().toString().replace("-", "")
                + EncodeUtil.md5(System.currentTimeMillis() + "").substring(0, 8);
        HealthCode healthCode = new HealthCode()
                .setHcode(codeType.getCode())
                .setType(type)
                .setApplyTime(new Date())
                .setQrcodePath(filePath)
                .setUserId(userId)
                .setState(1)
                .setApplyUserId(applyUserId)
                .setApplyPostId(postId)
                .setApplyCount(applyCount)
                .setFromId(fromId)
                .setSource(source)
                .setToken(tk)
                .setUpdateTime(new Date());
        String content = buildContentForHealthCode(healthCode, redirectUrl);
        createQrCode(codeType.getColor(), content, filePath);
        // 使原有健康码失效
        lambdaUpdate().eq(HealthCode::getUserId, userId).eq(HealthCode::getState, 1).set(HealthCode::getState, 2).update();
        // 保存新的健康码
        save(healthCode);
        return healthCode;
    }

    private String buildContentForHealthCode(HealthCode healthCode, String redirectUrl) {
        return healthCode.getToken();
//        return HttpUtil.appendUrlParam(redirectUrl, "tk", healthCode.getToken());
    }

    /**
     * 创建二维码
     */
    private void createQrCode(Color color, String content, String filePath) {
        log.info("生成二维码，内容：" + content);
        try (FileOutputStream fos = new FileOutputStream(filePath)) {
            QrCodeUtil.createQrCode(
                    Constants.QRCODE_FORMAT,
                    fos,
                    content,
                    color,
                    Color.WHITE,
                    null,//ImageIO.read(new ClassPathResource("qrlogo.jpg").getInputStream()),
                    Constants.QRCODE_SIZE);
        } catch (Exception e) {
            String err = "二维码生成失败，请稍后再试。";
            log.error(err, e);
            File f = new File(filePath);
            if (f.exists()) {
                f.delete();
            }
            throw new RuntimeException(err);
        }
    }

    private void checkApplyFormParam(UserApplyPost userApplyPost) throws BizException {
        if (userApplyPost.getHealthAbnormalDetail() == null) {
            userApplyPost.setHealthAbnormalDetail(Collections.emptyList());
        }
        if (userApplyPost.getFamilyMember() == null) {
            userApplyPost.setFamilyMember(Collections.emptyList());
        }
        Assert.isTrue(!ParamChecker.checkParamRequired(userApplyPost.getName()), "参数<姓名>不能为空。");
        userApplyPost.setName(userApplyPost.getName().replace(" ", ""));
        Assert.isTrue(!ParamChecker.checkParamRequired(userApplyPost.getCardType()), "参数<证件类型>不能为空。");
        Assert.isTrue(!ParamChecker.checkParamRequired(userApplyPost.getCardId()), "参数<证件号>不能为空。");
        Assert.isTrue(!IdCardUtil.IDCardValidate(userApplyPost.getCardId()).equals("true"), "身份证格式错误");
        Assert.isTrue(!ParamChecker.checkParamRequired(userApplyPost.getPhone()), "参数<手机号>不能为空。");
        Assert.isTrue(!userApplyPost.getPhone().matches(com.sdy.common.constant.Constants.REGEX_PHONE), "手机号格式错误");
        // 当前所在省市
        Assert.isTrue(!ParamChecker.checkParamRequired(userApplyPost.getTripAddr()), "参数<当前所在省市>错误。");
        Assert.isTrue(!ParamChecker.checkParamRequired(userApplyPost.getTripAddr().getArea()), "参数<当前所在地址>错误。");
        Assert.isTrue(userApplyPost.getTripAddr().getArea().contains(Address.SPLITTER_1), "参数<当前所在地址>包含非法字符。");
        Assert.isTrue(userApplyPost.getTripAddr().getArea().contains(Address.SPLITTER_2), "参数<当前所在地址>包含非法字符。");
        Assert.isTrue(!ParamChecker.checkParamRequired(userApplyPost.getTripAddr().getAddr()), "参数<当前所在地址>错误。");
        Assert.isTrue(userApplyPost.getTripAddr().getAddr().contains(Address.SPLITTER_1), "参数<当前所在地址>包含非法字符。");
        Assert.isTrue(userApplyPost.getTripAddr().getAddr().contains(Address.SPLITTER_2), "参数<当前所在地址>包含非法字符。");
        // 是否接触确诊病人
        Assert.isTrue(!ParamChecker.checkParamRequired(userApplyPost.getHealthTouchAddrDanger(), Constants.YES_CN, Constants.NO_CN), "参数<是否去过疫情地区>错误。");
        Assert.isTrue(!ParamChecker.checkParamRequired(userApplyPost.getHealthTouchPatient(), Constants.YES_CN, Constants.NO_CN), "参数<是否接触确诊病人>错误。");
        Assert.isTrue(!ParamChecker.checkParamRequired(userApplyPost.getHealthTouchWuhan(), Constants.YES_CN, Constants.NO_CN), "参数<是否接触武汉人员>错误。");
        for (String detail : userApplyPost.getHealthAbnormalDetail()) {
            Assert.isTrue(!ParamChecker.checkParamRequired(detail, Arrays.stream(HealthAbnormalType.values()).map(HealthAbnormalType::getType).toArray()), "参数<健康状况>错误。");
        }
        // 身份类型
        Assert.isTrue(!ParamChecker.checkParamRequired(userApplyPost.getType(), 1, 2, 3), "参数<身份类型>错误。");
        // 预约日期
        if (userApplyPost.getType().equals(1) || userApplyPost.getType().equals(2)) {
            Assert.isTrue(!ParamChecker.checkParamRequired(userApplyPost.getAppointmentTime()), "参数<预约日期>不能为空。");
        } else {
            userApplyPost.setAppointmentTime(null);
        }
        // 家属
        if (userApplyPost.getType().equals(1) || userApplyPost.getType().equals(2)) {
            for (FamilyMember familyMember : userApplyPost.getFamilyMember()) {
                Assert.isTrue(!ParamChecker.checkParamRequired(familyMember), "参数<家属>错误。");
                Assert.isTrue(!ParamChecker.checkParamRequired(familyMember.getName()), "参数<家属姓名>不能为空。");
                Assert.isTrue(!ParamChecker.checkParamRequired(familyMember.getPhone()), "参数<家属手机号>不能为空。");
                Assert.isTrue(familyMember.getName().contains(Address.SPLITTER_1), "参数<家属姓名>包含非法字符。");
                Assert.isTrue(familyMember.getName().contains(Address.SPLITTER_2), "参数<家属姓名>包含非法字符。");
                Assert.isTrue(!familyMember.getPhone().matches(com.sdy.common.constant.Constants.REGEX_PHONE), "家属手机号格式错误");
            }
        } else {
            // 所属医院部门
            Assert.isTrue(!ParamChecker.checkParamRequired(userApplyPost.getHsDepartmentId()), "参数<部门>不能为空。");
        }
        // 身份证图片
//        Assert.isTrue(userApplyPost.getIdCardImg() == null || userApplyPost.getIdCardImg().size() != 2, "请上传身份证正反面照片");

    }

    @Override
    public CodeCountPack getCodeCount(String code) {
        List<CodeCount> codeCount = healthCodeMapper.getCodeCount(code);
        CodeCount r = new CodeCount().setHcode(HealthCodeType.RED.getCode());
        CodeCount g = new CodeCount().setHcode(HealthCodeType.GREEN.getCode());
        CodeCount y = new CodeCount().setHcode(HealthCodeType.YELLOW.getCode());
        for (CodeCount count : codeCount) {
            if (count.getHcode().equals(HealthCodeType.RED.getCode())) {
                r.setCounts(count.getCounts());
            } else if (count.getHcode().equals(HealthCodeType.GREEN.getCode())) {
                g.setCounts(count.getCounts());
            } else if (count.getHcode().equals(HealthCodeType.YELLOW.getCode())) {
                y.setCounts(count.getCounts());
            }
        }
        return new CodeCountPack().setR(r).setG(g).setY(y);
    }

    @Override
    public void updateHealthCode(HealthCodeType newHealthCodeType, Integer userId, String source) {
        HealthCode healthCode = queryHealthCode(userId);
        if (healthCode == null) {
            return;
        }
        createHealthCode(newHealthCodeType,
                healthCode.getType(),
                userId,
                userId,
                healthCode.getApplyPostId(),
                null,
                null,
                null,
                source);
    }

    @Override
    public List<Map<String, Object>> getHospitalNum() {
        return healthCodeMapper.getHospitalNum();
    }
}
